
//--------------------------------------------------------------------
#include <avr/io.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "dkbasic.h"
#include "dkstring.h"
#include "dkexpression.h"
#include "dkvariables.h"
#include "dktables.h"
#include "dkprogram.h"

//--------------------------------------------------------------------

/*
  * based on code by Douglas R. Merritt, 1993
  * Transparent recursive descent expression parsing.
  */


char *expstr;


//--------------------------------------------------------------------
/* This just calls expr() and handles any following garbage */
dkexpression *expression( char *expressionstring )
{
	expstr = expressionstring;			// expstr is a global variable used by the expression parsing system

	return expr( NULL, NULL);
}



//--------------------------------------------------------------------
/*
  * Parse a single operand: "(expr)", "symbol", "123", "-expr".
  * Co-recursive with expr().
  */
dkexpression *item( void )
{
	char t[MAX_TOKEN_LENGTH+1];
	char t_lower[MAX_TOKEN_LENGTH+1];
    dkexpression *e;


	expstr = dkgettoken( expstr, t );	// get token from expstr into t
	strcpy( t_lower, t );
	strlwr( t_lower );

    if( (*t) == '(' )					// is token an open bracket?
	{
		e = expr( NULL, NULL ); 		/* nothing memoized */


		expstr = dkgettoken( expstr, t );	//get next token

		if( (*t) != ')' )
		{								//	error("Missing ending ')'");
			if( !dkstatus )	
				dkstatus = DKSTATUS_SYNTAXERROR;
			dkexpfree( e );
			return NULL;
		}

		return e;
	}
	else if ( class_unary( t_lower ) )
	{									// note: the way this is done is slightly broken
		e = item();						// as it is now, all unary operators have higher precedence than any binary operators
		if( !e )
		{		
			dkstatus = DKSTATUS_MISSINGOPERAND;
			return NULL;
		}
		return unary_op( t_lower, e );
	}
	else if( class_ident( t ) )
	{	
		e = dkidentifier( t );			// create expression and return it
		if( !e )
		{
			dkstatus = DKSTATUS_SYNTAXERROR;
			return NULL;
		}
		return e;
	}
	else if( !t[0] )
		return NULL;
//		dkstatus = DKSTATUS_MISSINGOPERAND;
	else if( t[0] == ':' )
		return NULL;


//	if( !dkstatus )	
//		dkstatus = DKSTATUS_SYNTAXERROR;

	return NULL;
}


//--------------------------------------------------------------------
/* The two parameters are memoization to avoid backtracking */
// a new dkexpression struct item will be malloc()'d and returned - it should be freed by caller when done with
dkexpression *expr( dkexpression *e1, char *op1 )
{
	dkexpression *e2;					/* read-ahead of a second operator and second */
	char op2[MAX_TOKEN_LENGTH+1];		/* operand */
	dkexpression *retval;
	char *local_op1=NULL;
	char *afterop2=NULL;
	char *sptr;
	uint8_t allocated_e1=0;


	if( !e1 )							// if we weren't passed e1 (expression), get an item
	{
		e1 = item();
		if( !e1 )		
			return NULL;
		allocated_e1 = 1;			// remember to dkexpfree(e1)
	}
	if( !op1 )
	{
		op1 = (char *)malloc( MAX_TOKEN_LENGTH+1 );
		if( !op1 )
		{
			if( allocated_e1 )			// remember to dkexpfree(e1)
				dkexpfree( e1 );
			if( !dkstatus )	
				dkstatus = DKSTATUS_OUTOFMEMORY;
			return NULL;				// memory allocation error
		}
		local_op1=op1;					// save local pointer to op1 to be free()'d later

		sptr = dkgettoken( expstr, op1 );	// get next token from expstr into op1 (operator)

		if( !strlen(op1) )
		{
			free( local_op1 );
			return e1;					// no more operators on this line - return the expression result
		}
 
		if( !class_binary( op1 ) )
		{
//			error("Expected operator");		// unless line was empty - check if gettoken returned 0
			free( local_op1 );
			return e1;
		}

		expstr = sptr;
	}

	e2 = item(); 						/* get second operand */
	if( !e2 )
	{
		if( !dkstatus )	
			dkstatus = DKSTATUS_SYNTAXERROR;
		if( allocated_e1 )			// remember to dkexpfree(e1)
			dkexpfree( e1 );
		if( local_op1 )
			free( local_op1 );
		return NULL;
	}


	afterop2 = dkgettoken( expstr, op2 );

	if( class_binary(op2) )
	{				
		expstr = afterop2;				// consume the characters in op2

		if( greater_precedence( op2, op1 ) )
		{
			retval = binary_op( e1, op1, expr(e2, op2) );
			if( local_op1 )
				free( local_op1 );
			if( e2 )
				dkexpfree( e2 );
			return retval;
		}
		else
		{
			retval = expr( binary_op(e1, op1, e2), op2 );
			if( local_op1 )
				free( local_op1 );
			if( e2 )
				dkexpfree( e2 );
			return retval;
		}
	}

	retval = binary_op(e1, op1, e2);	/* expr */

	if( local_op1 )
		free( local_op1 );
	if( e2 )
		dkexpfree( e2 );
//	if( allocated_e1 )			// remember to dkexpfree(e1)
//		dkexpfree( e1 );

	return retval;
}


//--------------------------------------------------------------------
//  * class_ident(c) Returns true if 'c' is identifier or constant.
uint8_t class_ident( char *c )
{
	if( ((*c)>='A' && (*c)<='Z') || ((*c)>='a' && (*c)<='z') )
		return 1;						// return true if the token starts with an alphabetic character A-Z/a-z
	if( ((*c)>='0' && (*c)<='9') )
		return 1;						// return true if the token starts with a digit 0-9
	switch( (*c) )
	{
		case '&':
		case '_':
		case '.':
		case '\"':
			return 1;						// return true if the token starts with &, _, .
	}
	return 0;
}


//--------------------------------------------------------------------
//  * class_binary(c) Returns true if 'c' is a binary operator
//  * (e.g. +, -, *, /, etc)
uint8_t class_binary( char *c )
{
	switch( (*c) )
	{
		case '=':
		case '+':
		case '-':
		case '*':
		case '/':
		case '<':
		case '>':
		case '\\':
			return 1;
	}


	if( c[0] == '\"' )
		return 0;

	strlwr( c );

	if( !strcmp_P( c, PSTR("and") ) )
		return 1;
	if( !strcmp_P( c, PSTR("or") ) )
		return 1;
	if( !strcmp_P( c, PSTR("xor") ) )
		return 1;
	if( !strcmp_P( c, PSTR("eqv") ) )
		return 1;
	if( !strcmp_P( c, PSTR("imp") ) )
		return 1;
	if( !strcmp_P( c, PSTR("mod") ) )
		return 1;


	return 0;
}


//--------------------------------------------------------------------
//  * greater_precedence(a, b)
//  * Returns true if the precedence of operator a is
//  * larger than that of b. (E.g. * is greater than +)
uint8_t greater_precedence( char *a, char *b )
{
	return op_precedence( a ) > op_precedence( b );
}


//--------------------------------------------------------------------
uint8_t op_precedence( char *a )
{
	uint8_t precedence=0;

	switch( a[0] )
	{
		case '^':
		case '*':
		case '/':
		case '\\':
			precedence = 50;
			return precedence;

		case '+':
		case '-':
			precedence = 30;
			return precedence;

		case '=':
		case '<':
		case '>':
			precedence = 10;
			return precedence;
	}


	if( !strcmp_P( a, PSTR("and") ) )
		precedence = 20;
	else if( !strcmp_P( a, PSTR("or") ) )
		precedence = 20;
	else if( !strcmp_P( a, PSTR("xor") ) )
		precedence = 20;
	else if( !strcmp_P( a, PSTR("eqv") ) )
		precedence = 20;
	else if( !strcmp_P( a, PSTR("imp") ) )
		precedence = 20;
	else if( !strcmp_P( a, PSTR("mod") ) )
		precedence = 20;

	return precedence;
}

//--------------------------------------------------------------------
//  * class_unary(c) Returns true if 'c' is a unary operator
//  * (e.g. -, !, ~)
uint8_t class_unary( char *c )
{
	switch( (*c) )
	{
		case '-':
			return 1;
	}

	if( !strcmp_P( c, PSTR("not") ) )
		return 1;

	return 0;
}


//--------------------------------------------------------------------
//  * unary_op(op, expr)
//  * Performs unary operation 'op' on expression 'expr'
//  * (or builds a unary syntax tree node)
dkexpression *unary_op( char *op, dkexpression *expr )
{
	if( !expr )
		return NULL;

	if( op[0] == '-' )
	{
		if( expr->type == DKVAR_BYTE )
			((uint8_t *)expr->data)[0] = 0 - ((uint8_t *)expr->data)[0];
		else
		{
			dkexptoint( expr );
			if( expr->type == DKVAR_INTEGER )
				((int16_t *)expr->data)[0] = 0 - ((int16_t *)expr->data)[0];
			else
			{
				if( !dkstatus )	
					dkstatus = DKSTATUS_TYPEMISMATCH;	// should we return here ?
			}
		}
	}
	else
	{
		if( !strcmp_P( op, PSTR("not") ) )				// bitwise NOT
		{
			if( expr->type == DKVAR_BYTE )
				((uint8_t *)expr->data)[0] ^= 0xFF;
			else
			{
				dkexptoint( expr );
				if( expr->type == DKVAR_INTEGER )
					((int16_t *)expr->data)[0] ^= 0xFFFF;
				else
				{
					if( !dkstatus )	
						dkstatus = DKSTATUS_TYPEMISMATCH;	// should we return here ?
				}
			}
		}
	}

	return expr;
}


//--------------------------------------------------------------------
//  * binary_op(expr1, op, expr2)
//  * Performs binary operation 'op' on expressions
//  * 'expr1' and 'expr2' (e.g. 5 * 3)
//  * (or builds a binary syntax tree node)
dkexpression *binary_op( dkexpression *expr1, char *op, dkexpression *expr2 )
{
	char *pstr;


	if( !expr1 )
		return NULL;
	if( !expr2 )
		return expr1;


	switch( (*op) )
	{
		case '=':
			if( expr1->type==DKVAR_STRING && expr2->type==DKVAR_STRING )
			{
				pstr = ((char **)expr1->data)[0];
				((int16_t *)expr1->data)[0] = !strcmp( pstr, ((char **)expr2->data)[0] );
				free( pstr );
				expr1->type = DKVAR_INTEGER;
			}
			else
			{
				dkexptoint( expr1 );
				dkexptoint( expr2 );
				if( expr1->type == DKVAR_INTEGER && expr2->type == DKVAR_INTEGER )
					((int16_t *)expr1->data)[0] = ( ((int16_t *)expr1->data)[0] == ((int16_t *)expr2->data)[0] );
				else
				{
					if( !dkstatus )	
						dkstatus = DKSTATUS_TYPEMISMATCH;	// should we return here ?
				}
			}
			break;
				

		case '<':
			if( op[1] == '>' )
			{												// op is <>
				if( expr1->type==DKVAR_STRING && expr2->type==DKVAR_STRING )
				{
					pstr = ((char **)expr1->data)[0];
					((int16_t *)expr1->data)[0] = strcmp( pstr, ((char **)expr2->data)[0] );
					free( pstr );
					expr1->type = DKVAR_INTEGER;
				}
				else
				{
					dkexptoint( expr1 );
					dkexptoint( expr2 );
					if( expr1->type == DKVAR_INTEGER && expr2->type == DKVAR_INTEGER )
						((int16_t *)expr1->data)[0] = ( ((int16_t *)expr1->data)[0] != ((int16_t *)expr2->data)[0] );
					else
					{
						if( !dkstatus )	
							dkstatus = DKSTATUS_TYPEMISMATCH;	// should we return here ?
					}
				}
				break;
			}
			if( op[1] == '=' )
			{												// op is <=
				if( expr1->type==DKVAR_STRING && expr2->type==DKVAR_STRING )
				{
					pstr = ((char **)expr1->data)[0];
					((int16_t *)expr1->data)[0] = ( strcmp( pstr, ((char **)expr2->data)[0] ) <=0 ? 1 : 0 );
					free( pstr );
					expr1->type = DKVAR_INTEGER;
				}
				else
				{
					dkexptoint( expr1 );
					dkexptoint( expr2 );
					if( expr1->type == DKVAR_INTEGER && expr2->type == DKVAR_INTEGER )
						((int16_t *)expr1->data)[0] = ( ((int16_t *)expr1->data)[0] <= ((int16_t *)expr2->data)[0] );
					else
					{
						if( !dkstatus )	
							dkstatus = DKSTATUS_TYPEMISMATCH;	// should we return here ?
					}
				}
				break;
			}

			if( expr1->type==DKVAR_STRING && expr2->type==DKVAR_STRING )
			{
				pstr = ((char **)expr1->data)[0];
				((int16_t *)expr1->data)[0] = ( strcmp( pstr, ((char **)expr2->data)[0] ) < 0 ? 1 : 0 );
				free( pstr );
				expr1->type = DKVAR_INTEGER;
			}
			else
			{
				dkexptoint( expr1 );
				dkexptoint( expr2 );
				if( expr1->type == DKVAR_INTEGER && expr2->type == DKVAR_INTEGER )
					((int16_t *)expr1->data)[0] = ( ((int16_t *)expr1->data)[0] < ((int16_t *)expr2->data)[0] );
				else
				{
					if( !dkstatus )	
						dkstatus = DKSTATUS_TYPEMISMATCH;	// should we return here ?
				}
			}
			break;


		case '>':
			if( op[1] == '=' )
			{												// op is >=
				if( expr1->type==DKVAR_STRING && expr2->type==DKVAR_STRING )
				{
					pstr = ((char **)expr1->data)[0];
					((int16_t *)expr1->data)[0] = ( strcmp( pstr, ((char **)expr2->data)[0] ) >= 0 ? 1 : 0 );
					free( pstr );
					expr1->type = DKVAR_INTEGER;
				}
				else
				{
					dkexptoint( expr1 );
					dkexptoint( expr2 );
					if( expr1->type == DKVAR_INTEGER && expr2->type == DKVAR_INTEGER )
						((int16_t *)expr1->data)[0] = ( ((int16_t *)expr1->data)[0] >= ((int16_t *)expr2->data)[0] );
					else
					{
						if( !dkstatus )	
							dkstatus = DKSTATUS_TYPEMISMATCH;	// should we return here ?
					}
				}
				break;
			}

			if( expr1->type==DKVAR_STRING && expr2->type==DKVAR_STRING )
			{
				pstr = ((char **)expr1->data)[0];
				((int16_t *)expr1->data)[0] = ( strcmp( pstr, ((char **)expr2->data)[0] ) > 0 ? 1 : 0 );
				free( pstr );
				expr1->type = DKVAR_INTEGER;
			}
			else
			{
				dkexptoint( expr1 );
				dkexptoint( expr2 );
				if( expr1->type == DKVAR_INTEGER && expr2->type == DKVAR_INTEGER )
					((int16_t *)expr1->data)[0] = ( ((int16_t *)expr1->data)[0] > ((int16_t *)expr2->data)[0] );
				else
				{
					if( !dkstatus )	
						dkstatus = DKSTATUS_TYPEMISMATCH;	// should we return here ?
				}
			}
			break;


		case '+':
			if( expr1->type==DKVAR_STRING && expr2->type==DKVAR_STRING )
			{
				pstr = (char *)malloc( strlen( ((char **)expr1->data)[0] ) + strlen( ((char **)expr2->data)[0] ) + 1 );
				if( !pstr )
				{
					dkstatus = DKSTATUS_OUTOFMEMORY;
					return expr1;
				}
				strcpy( pstr, ((char **)expr1->data)[0] );
				strcat( pstr, ((char **)expr2->data)[0] );
				free( ((char **)expr1->data)[0] );
				((int16_t *)expr1->data)[0] = (uint16_t)pstr;
			}
			else if( expr1->type==DKVAR_FLOATSINGLE )
			{
				dkexptofloatsingle( expr2 );
				if( expr2->type != DKVAR_FLOATSINGLE )
				{
					if( !dkstatus )
						dkstatus = DKSTATUS_TYPEMISMATCH;
				}
				((double *)expr1->data)[0] += ((double *)expr2->data)[0];
			}
			else if( expr1->type==DKVAR_FLOATDOUBLE )
			{
				dkexptofloatdouble( expr2 );
				if( expr2->type != DKVAR_FLOATDOUBLE )
				{
					if( !dkstatus )
						dkstatus = DKSTATUS_TYPEMISMATCH;
				}
				((double *)expr1->data)[0] += ((double *)expr2->data)[0];
			}
			else if( expr1->type==DKVAR_BYTE )
			{
				dkexptobyte( expr2 );
				if( expr2->type != DKVAR_BYTE )
				{
					if( !dkstatus )
						dkstatus = DKSTATUS_TYPEMISMATCH;
				}
				((uint8_t *)expr1->data)[0] += ((uint8_t *)expr2->data)[0];
			}
			else
			{
				dkexptoint( expr1 );
				dkexptoint( expr2 );
				if( expr1->type == DKVAR_INTEGER && expr2->type == DKVAR_INTEGER )
					((int16_t *)expr1->data)[0] += ((int16_t *)expr2->data)[0];
				else
				{
					if( !dkstatus )	
						dkstatus = DKSTATUS_TYPEMISMATCH;	// should we return here ?
				}
			}
			break;


		case '-':
			if( expr1->type==DKVAR_FLOATSINGLE )
			{
				dkexptofloatsingle( expr2 );
				if( expr2->type != DKVAR_FLOATSINGLE )
				{
					if( !dkstatus )
						dkstatus = DKSTATUS_TYPEMISMATCH;
				}
				((double *)expr1->data)[0] -= ((double *)expr2->data)[0];
			}
			else if( expr1->type==DKVAR_FLOATDOUBLE )
			{
				dkexptofloatdouble( expr2 );
				if( expr2->type != DKVAR_FLOATDOUBLE )
				{
					if( !dkstatus )
						dkstatus = DKSTATUS_TYPEMISMATCH;
				}
				((double *)expr1->data)[0] -= ((double *)expr2->data)[0];
			}
			else if( expr1->type==DKVAR_BYTE )
			{
				dkexptobyte( expr2 );
				if( expr2->type != DKVAR_BYTE )
				{
					if( !dkstatus )
						dkstatus = DKSTATUS_TYPEMISMATCH;
				}
				((uint8_t *)expr1->data)[0] -= ((uint8_t *)expr2->data)[0];
			}
			else
			{
				dkexptoint( expr1 );
				dkexptoint( expr2 );
				if( expr1->type == DKVAR_INTEGER && expr2->type == DKVAR_INTEGER )
					((int16_t *)expr1->data)[0] -= ((int16_t *)expr2->data)[0];
				else
				{
					if( !dkstatus )	
						dkstatus = DKSTATUS_TYPEMISMATCH;	// should we return here ?
				}
			}
			break;


		case '*':
			if( expr1->type==DKVAR_FLOATSINGLE )
			{
				dkexptofloatsingle( expr2 );
				if( expr2->type != DKVAR_FLOATSINGLE )
				{
					if( !dkstatus )
						dkstatus = DKSTATUS_TYPEMISMATCH;
				}
				((double *)expr1->data)[0] *= ((double *)expr2->data)[0];
			}
			else if( expr1->type==DKVAR_FLOATDOUBLE )
			{
				dkexptofloatdouble( expr2 );
				if( expr2->type != DKVAR_FLOATDOUBLE )
				{
					if( !dkstatus )
						dkstatus = DKSTATUS_TYPEMISMATCH;
				}
				((double *)expr1->data)[0] *= ((double *)expr2->data)[0];
			}
			else if( expr1->type==DKVAR_BYTE )
			{
				dkexptobyte( expr2 );
				if( expr2->type != DKVAR_BYTE )
				{
					if( !dkstatus )
						dkstatus = DKSTATUS_TYPEMISMATCH;
				}
				((uint8_t *)expr1->data)[0] *= ((uint8_t *)expr2->data)[0];
			}
			else
			{
				dkexptoint( expr1 );
				dkexptoint( expr2 );
				if( expr1->type == DKVAR_INTEGER && expr2->type == DKVAR_INTEGER )
					((int16_t *)expr1->data)[0] *= ((int16_t *)expr2->data)[0];
				else
				{
					if( !dkstatus )	
						dkstatus = DKSTATUS_TYPEMISMATCH;	// should we return here ?
				}
			}
			break;


		case '/':
			if( expr1->type==DKVAR_FLOATSINGLE )
			{
				dkexptofloatsingle( expr2 );
				if( expr2->type != DKVAR_FLOATSINGLE )
				{
					if( !dkstatus )
						dkstatus = DKSTATUS_TYPEMISMATCH;
				}
				((double *)expr1->data)[0] /= ((double *)expr2->data)[0];
			}
			else if( expr1->type==DKVAR_FLOATDOUBLE )
			{
				dkexptofloatdouble( expr2 );
				if( expr2->type != DKVAR_FLOATDOUBLE )
				{
					if( !dkstatus )
						dkstatus = DKSTATUS_TYPEMISMATCH;
				}
				((double *)expr1->data)[0] /= ((double *)expr2->data)[0];
			}
			else if( expr1->type==DKVAR_BYTE )
			{
				dkexptobyte( expr2 );
				if( expr2->type != DKVAR_BYTE )
				{
					if( !dkstatus )
						dkstatus = DKSTATUS_TYPEMISMATCH;
				}
				((uint8_t *)expr1->data)[0] /= ((uint8_t *)expr2->data)[0];
			}
			else
			{
				dkexptoint( expr1 );
				dkexptoint( expr2 );
				if( expr1->type == DKVAR_INTEGER && expr2->type == DKVAR_INTEGER )
					((int16_t *)expr1->data)[0] /= ((int16_t *)expr2->data)[0];
				else
				{
					if( !dkstatus )	
						dkstatus = DKSTATUS_TYPEMISMATCH;	// should we return here ?
				}
			}
			break;


		case '\\':											// \ integer division (for floats - round each operand, then truncate result to int)
			if( expr1->type==DKVAR_FLOATSINGLE )
			{
				dkexptofloatsingle( expr2 );
				if( expr2->type != DKVAR_FLOATSINGLE )
				{
					if( !dkstatus )
						dkstatus = DKSTATUS_TYPEMISMATCH;
				}
				((double *)expr1->data)[0] /= ((double *)expr2->data)[0];
			}
			else if( expr1->type==DKVAR_FLOATDOUBLE )
			{
				dkexptofloatdouble( expr2 );
				if( expr2->type != DKVAR_FLOATDOUBLE )
				{
					if( !dkstatus )
						dkstatus = DKSTATUS_TYPEMISMATCH;
				}
				((double *)expr1->data)[0] /= ((double *)expr2->data)[0];
			}
			else if( expr1->type==DKVAR_BYTE )
			{
				dkexptobyte( expr2 );
				if( expr2->type != DKVAR_BYTE )
				{
					if( !dkstatus )
						dkstatus = DKSTATUS_TYPEMISMATCH;
				}
				((uint8_t *)expr1->data)[0] /= ((uint8_t *)expr2->data)[0];
			}
			else
			{
				dkexptoint( expr1 );
				dkexptoint( expr2 );
				if( expr1->type == DKVAR_INTEGER && expr2->type == DKVAR_INTEGER )
					((int16_t *)expr1->data)[0] /= ((int16_t *)expr2->data)[0];
				else
				{
					if( !dkstatus )	
						dkstatus = DKSTATUS_TYPEMISMATCH;	// should we return here ?
				}
			}
			break;


		case '^':
			if( expr1->type==DKVAR_STRING || expr2->type==DKVAR_STRING )
			{
				if( !dkstatus )	
					dkstatus = DKSTATUS_TYPEMISMATCH;	// should we return here ?
			}
			else
			{
				dkexptofloatsingle( expr1 );
				dkexptofloatsingle( expr2 );
//				((int16_t *)expr1->data)[0] = pow( ((int16_t *)expr1->data)[0], ((double *)expr2->data)[0] );
			}
			break;

		default:
			strlwr( op );

			if( !strcmp_P( op, PSTR("and") ) )				// bitwise AND
			{
				if( expr1->type == DKVAR_STRING || expr2->type==DKVAR_STRING )
				{
					if( !dkstatus )	
						dkstatus = DKSTATUS_TYPEMISMATCH;	// should we return here ?
				}
				else if( expr1->type == DKVAR_BYTE )
				{
					dkexptobyte( expr2 );
					if( expr2->type != DKVAR_BYTE )
					{
						if( !dkstatus )
							dkstatus = DKSTATUS_TYPEMISMATCH;
					}
					((int8_t *)expr1->data)[0] &= ((int8_t *)expr2->data)[0];
				}
				else	
				{
					dkexptoint( expr1 );
					dkexptoint( expr2 );
					((int16_t *)expr1->data)[0] &= ((int16_t *)expr2->data)[0];
				}
			}
			else if( !strcmp_P( op, PSTR("or") ) )				// bitwise OR
			{
				if( expr1->type == DKVAR_STRING || expr2->type==DKVAR_STRING )
				{
					if( !dkstatus )	
						dkstatus = DKSTATUS_TYPEMISMATCH;	// should we return here ?
				}
				else if( expr1->type == DKVAR_BYTE )
				{
					dkexptobyte( expr2 );
					if( expr2->type != DKVAR_BYTE )
					{
						if( !dkstatus )
							dkstatus = DKSTATUS_TYPEMISMATCH;
					}
					((int8_t *)expr1->data)[0] |= ((int8_t *)expr2->data)[0];
				}
				else	
				{
					dkexptoint( expr1 );
					dkexptoint( expr2 );
					((int16_t *)expr1->data)[0] |= ((int16_t *)expr2->data)[0];
				}
			}
			else if( !strcmp_P( op, PSTR("xor") ) )				// bitwise XOR
			{
				if( expr1->type == DKVAR_STRING || expr2->type==DKVAR_STRING )
				{
					if( !dkstatus )	
						dkstatus = DKSTATUS_TYPEMISMATCH;	// should we return here ?
				}
				else if( expr1->type == DKVAR_BYTE )
				{
					dkexptobyte( expr2 );
					if( expr2->type != DKVAR_BYTE )
					{
						if( !dkstatus )
							dkstatus = DKSTATUS_TYPEMISMATCH;
					}
					((int8_t *)expr1->data)[0] ^= ((int8_t *)expr2->data)[0];
				}
				else	
				{
					dkexptoint( expr1 );
					dkexptoint( expr2 );
					((int16_t *)expr1->data)[0] ^= ((int16_t *)expr2->data)[0];
				}
			}
			else if( !strcmp_P( op, PSTR("eqv") ) )				// bitwise EQV
			{
				if( expr1->type == DKVAR_STRING || expr2->type==DKVAR_STRING )
				{
					if( !dkstatus )	
						dkstatus = DKSTATUS_TYPEMISMATCH;	// should we return here ?
				}
				else if( expr1->type == DKVAR_BYTE )
				{
					dkexptobyte( expr2 );
					if( expr2->type != DKVAR_BYTE )
					{
						if( !dkstatus )
							dkstatus = DKSTATUS_TYPEMISMATCH;
					}
					((int8_t *)expr1->data)[0] ^= ((int8_t *)expr2->data)[0];
					((int8_t *)expr1->data)[0] ^= 0xFF;
				}
				else	
				{
					dkexptoint( expr1 );
					dkexptoint( expr2 );
					((int16_t *)expr1->data)[0] ^= ((int16_t *)expr2->data)[0];
					((int16_t *)expr1->data)[0] ^= 0xFFFF;
				}
			}
			else if( !strcmp_P( op, PSTR("imp") ) )				// bitwise IMP
			{
				if( expr1->type == DKVAR_STRING || expr2->type==DKVAR_STRING )
				{
					if( !dkstatus )	
						dkstatus = DKSTATUS_TYPEMISMATCH;	// should we return here ?
				}
				else if( expr1->type == DKVAR_BYTE )
				{
					dkexptobyte( expr2 );
					if( expr2->type != DKVAR_BYTE )
					{
						if( !dkstatus )
							dkstatus = DKSTATUS_TYPEMISMATCH;
					}
					((int8_t *)expr1->data)[0] ^= ((int8_t *)expr2->data)[0];
					((int8_t *)expr1->data)[0] ^= 0xFF;
					((int8_t *)expr1->data)[0] |= ((int8_t *)expr2->data)[0];
				}
				else	
				{
					dkexptoint( expr1 );
					dkexptoint( expr2 );
					((int16_t *)expr1->data)[0] ^= ((int16_t *)expr2->data)[0];
					((int16_t *)expr1->data)[0] ^= 0xFFFF;
					((int16_t *)expr1->data)[0] |= ((int16_t *)expr2->data)[0];
				}
			}
			if( !strcmp_P( op, PSTR("mod") ) )				// MOD
			{
				if( expr1->type == DKVAR_STRING || expr2->type==DKVAR_STRING )
				{
					if( !dkstatus )	
						dkstatus = DKSTATUS_TYPEMISMATCH;	// should we return here ?
				}
				else if( expr1->type == DKVAR_BYTE )
				{
					dkexptobyte( expr2 );
					if( expr2->type != DKVAR_BYTE )
					{
						if( !dkstatus )
							dkstatus = DKSTATUS_TYPEMISMATCH;
					}
					((int8_t *)expr1->data)[0] %= ((int8_t *)expr2->data)[0];
				}
				else	
				{
					dkexptoint( expr1 );
					dkexptoint( expr2 );
					((int16_t *)expr1->data)[0] %= ((int16_t *)expr2->data)[0];
				}
			}
			break;
	}

	return expr1;
}


//--------------------------------------------------------------------
dkexpression *dkidentifier( char *t )
{
	uint16_t nameflags;
	char *sptr;
	uint8_t tlen;
	char t2[MAX_TOKEN_LENGTH+1];
	dkexpression *e=NULL;
	dkexpression *varexp=NULL;
	struct _dkvariable *var=NULL;
	dkexpression * (*functionhandler)(char *str, uint16_t nameflags) = NULL;
	uint16_t vardims[DK_MAX_ARRAY_DIMS];				// up to 8-dimension arrays supported (arbitrary maximum)
	int numdims;

										// check what is in t and process it accordingly - constant / variable / function

	if( (*t)>='0' && (*t)<='9' )		// if first character is a digit, process as single float
	{
		e = dknewexp();
		if( !e )
		{
			if( !dkstatus )	
				dkstatus = DKSTATUS_OUTOFMEMORY;
			return NULL;				// memory allocation error
		}

		e->data = (double *)malloc( sizeof(double) );
		if( !((double *)e->data) )
		{
			if( !dkstatus )	
				dkstatus = DKSTATUS_OUTOFMEMORY;
			return NULL;				// memory allocation error
		}
		((double *)e->data)[0] = (double)atof( t );
		e->type = DKVAR_FLOATSINGLE;		// set expression type = floatsingle
	}
	else if( (*t)=='&' )				// first character is &, process as octal int
	{
		e = dknewexp();
		if( !e )
		{
			if( !dkstatus )	
				dkstatus = DKSTATUS_OUTOFMEMORY;
			return NULL;				// memory allocation error
		}
		e->type = DKVAR_INTEGER;	

		e->data = (int16_t *)malloc( sizeof(int16_t) );
		if( !((int16_t *)e->data) )
		{
			if( !dkstatus )	
				dkstatus = DKSTATUS_OUTOFMEMORY;
			return NULL;				// memory allocation error
		}
		t2[0] = '&';
		expstr = dkgettoken( expstr, t2+1 );	// get next token after & into t2 (token2)
		if( !strlen(t2) )
			return e;					// unable to get token after & - return 0

		((int16_t *)e->data)[0] = dkstrtonum( t2 );
	}
	else if( (t[0]>='A'&&t[0]<='Z') || (t[0]>='a'&&t[0]<='z') )
	{

		strlwr( t );					// convert token to lowercase
		expstr = dkgetnameflags( expstr, &nameflags );
										// string t holds var name
										// nameflags holds var flags
// if DKNAME_PAREN bit set in nameflags, should read bracket and numeric value(s)
// A(3)  A(2,1)

		functionhandler = dkfindfunction( t );	// check if token matches a function name in dkfunctiontable
		if( functionhandler )
		{
			e = functionhandler( expstr, nameflags );
			return e;					// return expression result from function handler
		}

										// the name is not in function list, check variables for a matching name
		if( !(nameflags & (DKNAME_STRING|DKNAME_INTEGER|DKNAME_FLOATSINGLE|DKNAME_FLOATDOUBLE|DKNAME_BYTE)) )
			nameflags |= DKVAR_DEFAULT;

// check for DKNAME_PAREN and look for variable array
		if( nameflags & DKNAME_PAREN )
		{
			numdims=0;
			
			e = dkgetfirstexpression( expstr );
			if( e )
			{
				dkexptoint(e);
				if( e->type != DKVAR_INTEGER )
				{
					if( !dkstatus )	
						dkstatus = DKSTATUS_SYNTAXERROR;
					dkexpfree(e);
					return NULL;
				}
				vardims[numdims] = ((uint16_t *)e->data)[0];
				numdims++;
				dkexpfree(e);

				while( (e = dkgetnextexpression( expstr )) && numdims<DK_MAX_ARRAY_DIMS )
				{
					dkexptoint(e);
					if( e->type != DKVAR_INTEGER )
					{
						if( !dkstatus )	
							dkstatus = DKSTATUS_SYNTAXERROR;
						dkexpfree(e);
						return NULL;
					}
					vardims[numdims] = ((uint16_t *)e->data)[0];
					numdims++;
					dkexpfree(e);				
				}
			}
			
			varexp = dkgetexpfromarray( t, nameflags, numdims, vardims );
		}
		else										// not array (no parenthesis)
		{
			var = dkgetvariable( t, nameflags );	// get variable named t
			if( var )
			{
//				varexp = &var->e;
                varexp = (dkexpression *)malloc( sizeof(dkexpression) );
                memcpy( varexp, &var->e, sizeof(dkexpression) );  
            }
		}

		e = dknewexp();
		if( !e )
		{
			if( !dkstatus )	
				dkstatus = DKSTATUS_OUTOFMEMORY;
			return NULL;				// memory allocation error
		}

	
		if( varexp )
		{											// a variable matching the name was found, return variable's value
			e->type = varexp->type;
			switch( e->type )
			{
				case DKVAR_STRING:
					e->data = (char **)malloc( sizeof(char *) );
					if( !((char **)e->data) )
					{
						dkexpfree(e);			// malloc error
						if( !dkstatus )	
							dkstatus = DKSTATUS_OUTOFMEMORY;
						return NULL;
					}
					((char **)e->data)[0] = (char *)malloc( strlen(((char **)varexp->data)[0])+1 );
					strcpy( ((char **)e->data)[0], ((char **)varexp->data)[0] );
					break;
					
				case DKVAR_BYTE:
					e->data = (int8_t *)malloc( sizeof( int8_t ) );
					if( !((int8_t *)e->data) )
					{
						dkexpfree(e);			// malloc error
						if( !dkstatus )	
							dkstatus = DKSTATUS_OUTOFMEMORY;
						return NULL;
					}
					((int8_t *)e->data)[0] = ((int8_t *)varexp->data)[0];
					break;
					
				case DKVAR_INTEGER:
					e->data = (int16_t *)malloc( sizeof( int16_t ) );
					if( !((int16_t *)e->data) )
					{
						dkexpfree(e);			// malloc error
						if( !dkstatus )	
							dkstatus = DKSTATUS_OUTOFMEMORY;
						return NULL;
					}
					((int16_t *)e->data)[0] = ((int16_t *)varexp->data)[0];
					break;
					
				case DKVAR_FLOATSINGLE:
				case DKVAR_FLOATDOUBLE:
					e->data = (double *)malloc( sizeof( double ) );
					if( !((double *)e->data) )
					{
						dkexpfree(e);			// malloc error
						if( !dkstatus )	
							dkstatus = DKSTATUS_OUTOFMEMORY;
						return NULL;
					}
					((double *)e->data)[0] = ((double *)varexp->data)[0];
					break;
			}
			free( varexp );
		}
		else
		{												// no variable found by that name, return default value for that type
			if( nameflags & DKNAME_STRING )
				e->type = DKVAR_STRING;
			else if( nameflags & DKNAME_INTEGER )
				e->type = DKVAR_INTEGER;
			else if( nameflags & DKNAME_FLOATSINGLE )
				e->type = DKVAR_FLOATSINGLE;
			else if( nameflags & DKNAME_FLOATDOUBLE )
				e->type = DKVAR_FLOATDOUBLE;
			else if( nameflags & DKNAME_BYTE )
				e->type = DKVAR_BYTE;
			else
				e->type = DKVAR_DEFAULT;


			switch( e->type )
			{
				case DKVAR_STRING:
					e->data = (char **)malloc( sizeof( char * ) );
					if( !((char **)e->data) )
					{
						dkexpfree(e);			// malloc error
						if( !dkstatus )	
							dkstatus = DKSTATUS_OUTOFMEMORY;
						return NULL;
					}
					((char **)e->data)[0] = (char *)malloc( 1 );
					((char **)e->data)[0][0] = '\0';
					break;
					
				case DKVAR_BYTE:
					e->data = (int8_t *)malloc( sizeof( int8_t ) );
					if( !((int8_t *)e->data) )
					{
						dkexpfree(e);			// malloc error
						if( !dkstatus )	
							dkstatus = DKSTATUS_OUTOFMEMORY;
						return NULL;
					}
					((int8_t *)e->data)[0] = dkfindconstant( t );			// set value to result from constanttable in flash
					break;
					
				case DKVAR_INTEGER:
					e->data = (int16_t *)malloc( sizeof( int16_t ) );
					if( !((int16_t *)e->data) )
					{
						dkexpfree(e);			// malloc error
						if( !dkstatus )	
							dkstatus = DKSTATUS_OUTOFMEMORY;
						return NULL;
					}
					((int16_t *)e->data)[0] = dkfindconstant( t );			// set value to result from constanttable in flash
					break;
					
				case DKVAR_FLOATSINGLE:
				case DKVAR_FLOATDOUBLE:
					e->data = (double *)malloc( sizeof( double ) );
					if( !((double *)e->data) )
					{
						dkexpfree(e);			// malloc error
						if( !dkstatus )	
							dkstatus = DKSTATUS_OUTOFMEMORY;
						return NULL;
					}
					((double *)e->data)[0] = dkfindconstant( t );
					break;
			}
		}
	}
	else if( t[0]=='\"' )
	{
		tlen = strlen(t);
		if( t[tlen-1] != '\"' )			// check for closing quote
			tlen++;						// add one to tlen to compensate for missing closing "

		e = dknewexp();
		if( !e )
		{
			if( !dkstatus )	
				dkstatus = DKSTATUS_OUTOFMEMORY;
			return NULL;				// memory allocation error
		}
		e->data = (char **)malloc(sizeof(char *));

		if( tlen > 2 )
		{
			sptr = (char *)malloc( tlen-2+1 );	// allocate space for token string without quotes (plus NULL terminator)
			if( !sptr )
			{
				free( e );
				if( !dkstatus )	
					dkstatus = DKSTATUS_OUTOFMEMORY;
				return NULL;				// memory allocation error
			}

			strncpy( sptr, t+1, tlen-2);	// copy string into newly allocated memory, don't copy quotes
			sptr[tlen-2] = '\0';			// make sure string is null-terminated
			((char **)e->data)[0] = sptr;
		}
		else
		{
			sptr = (char *)malloc( 1 );		// allocate space for token string without quotes (plus NULL terminator)
			if( !sptr )
			{
				free( e );
				if( !dkstatus )	
					dkstatus = DKSTATUS_OUTOFMEMORY;
				return NULL;				// memory allocation error
			}

			sptr[0] = '\0';			// make sure string is null-terminated
			((char **)e->data)[0] = sptr;
		}

		e->type = DKVAR_STRING;
	}

	return e;
}


//--------------------------------------------------------------------
// free dkexpression e and the memory allocated to 'value' if non-integer
void dkexpfree( dkexpression *e )
{
	if( !e )
		return;

	if( e->type == DKVAR_STRING )
		free( ((char **)e->data)[0] );

	if( e->data )
		free( e->data );

	free( e );

	return;	
}


//--------------------------------------------------------------------
// allocate memory for a new dkexpression and set it to empty values
dkexpression *dknewexp( void )
{
	dkexpression *e;
	
	e = malloc( sizeof( dkexpression ) );
	if( !e )
		return NULL;

	e->type = 0;
	e->data = NULL;
	e->numdimensions = 0;
	e->dimensions = NULL;
	

	return e;
}


//--------------------------------------------------------------------
// return value of e as uint16_t and set *exptype to indicate what type of expression e is (0 if unable to convert to int)
/*
uint16_t dkexpint( dkexpression *e, uint16_t *exptype )
{
	(*exptype) = 0;

	if( !e )
		return 0;

	if( e->type == DKVAR_INTEGER )
	{
		(*exptype) = e->type;
		return ((int16_t *)e->data)[0];
	}
	if( e->type == DKVAR_BYTE )
	{
		(*exptype) = e->type;
		return ((int8_t *)e->data)[0];
	}
	if( e->type == DKVAR_FLOATSINGLE || e->type == DKVAR_FLOATDOUBLE )
	{
		(*exptype) = e->type;
		return (uint16_t) round( ((double *)e->data)[0] );
	}


	return 0;
}
*/

//--------------------------------------------------------------------

double dkexptofloatsingle( dkexpression *e )
{
	dkexptofloatdouble( e );
	if( e->type == DKVAR_FLOATDOUBLE )
		e->type = DKVAR_FLOATSINGLE;

	return ((double *)e->data)[0];
}


//--------------------------------------------------------------------

double dkexptofloatdouble( dkexpression *e )
{
	double evalue;


	if( !e )
		return 0;
		
	if( e->type == DKVAR_FLOATSINGLE )
		e->type = DKVAR_FLOATDOUBLE;
		
	if( e->type == DKVAR_FLOATDOUBLE )
		return ((double *)e->data)[0];

	if( e->type == DKVAR_INTEGER )
	{
		evalue = ((int16_t *)e->data)[0];
		if( e->data )
			free( e->data );
		e->data = (double *)malloc( sizeof(double) );
		if( !e->data )
		{
			if( !dkstatus )
				dkstatus = DKSTATUS_OUTOFMEMORY;
			return 0;
		}
		((double *)e->data)[0] = evalue;
		e->type = DKVAR_FLOATDOUBLE;
		
		return evalue;
	}
	
	if( e->type == DKVAR_BYTE )
	{
		evalue = ((uint8_t *)e->data)[0];
		if( e->data )
			free( e->data );
		e->data = (double *)malloc( sizeof(double) );
		if( !e->data )
		{
			if( !dkstatus )
				dkstatus = DKSTATUS_OUTOFMEMORY;
			return 0;
		}
		((double *)e->data)[0] = evalue;
		e->type = DKVAR_FLOATDOUBLE;
		
		return evalue;
	}

	if( e->type == DKVAR_FLOATDOUBLE )
	{
		evalue = ((int8_t *)e->data)[0];
		if( e->data )
			free( e->data );
		e->data = (double *)malloc( sizeof(double) );
		if( !e->data )
		{
			if( !dkstatus )
				dkstatus = DKSTATUS_OUTOFMEMORY;
			return 0;
		}
		((double *)e->data)[0] = evalue;
		e->type = DKVAR_FLOATDOUBLE;
		
		return evalue;
	}


	return 0;
}


//--------------------------------------------------------------------
uint16_t dkexptoint( dkexpression *e )
{
	int16_t evalue;


	if( !e )
		return 0;
		
	if( e->type == DKVAR_INTEGER )
		return ((int16_t *)e->data)[0];

	if( e->type == DKVAR_BYTE )
	{
		evalue = ((uint8_t *)e->data)[0];
		if( e->data )
			free( e->data );
		e->data = (int16_t *)malloc( sizeof(int16_t) );
		if( !e->data )
		{
			if( !dkstatus )
				dkstatus = DKSTATUS_OUTOFMEMORY;
			return 0;
		}
		((int16_t *)e->data)[0] = evalue;
		e->type = DKVAR_INTEGER;
		
		return evalue;
	}

	if( e->type == DKVAR_FLOATSINGLE || e->type == DKVAR_FLOATDOUBLE )
	{
		evalue = round( ((double *)e->data)[0] );		// save float value in integer
		if( e->data )
			free( e->data );
		e->data = (int16_t *)malloc( sizeof(int16_t) );
		if( !((int16_t *)e->data) )
		{
			if( !dkstatus )	
				dkstatus = DKSTATUS_OUTOFMEMORY;
			return 0;				// memory allocation error
		}
		((int16_t *)e->data)[0] = evalue;
		e->type = DKVAR_INTEGER;
		
		return evalue;
	}


	return 0;
}


//--------------------------------------------------------------------

uint8_t dkexptobyte( dkexpression *e )
{
	int8_t evalue;


	if( !e )
		return 0;
		
	if( e->type == DKVAR_BYTE )
		return ((int8_t *)e->data)[0];

	if( e->type == DKVAR_INTEGER )
	{
		evalue = ((int16_t *)e->data)[0];
		if( e->data )
			free( e->data );
		e->data = (int8_t *)malloc( sizeof(int8_t) );
		if( !e->data )
		{
			if( !dkstatus )
				dkstatus = DKSTATUS_OUTOFMEMORY;
			return 0;
		}
		((int8_t *)e->data)[0] = evalue;
		e->type = DKVAR_BYTE;
		
		return evalue;
	}

	if( e->type == DKVAR_FLOATSINGLE || e->type == DKVAR_FLOATDOUBLE )
	{
		evalue = round( ((double *)e->data)[0] );		// save float value in integer
		if( e->data )
			free( e->data );
		e->data = (int8_t *)malloc( sizeof(int8_t) );
		if( !((int8_t *)e->data) )
		{
			if( !dkstatus )	
				dkstatus = DKSTATUS_OUTOFMEMORY;
			return 0;				// memory allocation error
		}
		((int8_t *)e->data)[0] = evalue;
		e->type = DKVAR_BYTE;
		
		return evalue;
	}


	return 0;
}


//--------------------------------------------------------------------

